package models

import (
	"context"
	"fmt"
	"time"

	"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
	"github.com/shopspring/decimal"
)

type Kline struct {
	Symbol    string          `ch:"symbol"`
	Period    string          `ch:"period"`
	Timestamp time.Time       `ch:"timestamp"`
	Open      decimal.Decimal `ch:"open"`
	High      decimal.Decimal `ch:"high"`
	Low       decimal.Decimal `ch:"low"`
	Close     decimal.Decimal `ch:"close"`
	Volume    int64           `ch:"volume"`
	Amount    decimal.Decimal `ch:"amount"`
}

type KlineModel struct {
	ctx        context.Context
	clickhouse driver.Conn
}

func NewKlineModel(ctx context.Context, clickhouse driver.Conn) *KlineModel {
	return &KlineModel{ctx: ctx, clickhouse: clickhouse}
}

func (k *KlineModel) IsSynced(symbol string, period string) bool {
	var count int
	err := k.clickhouse.Select(k.ctx, &count, "SELECT COUNT(*) FROM kline WHERE symbol =? AND period =?", symbol, period)
	if err != nil {
		return false
	}

	return count > 0
}

func (k *KlineModel) Insert(kline *Kline) error {
	err := k.clickhouse.Exec(k.ctx, "INSERT INTO kline (symbol, period, timestamp, open, high, low, close, volume, amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
		kline.Symbol, kline.Period, kline.Timestamp, kline.Open, kline.High, kline.Low, kline.Close, kline.Volume, kline.Amount)
	if err != nil {
		return fmt.Errorf("insert kline error: %v", err)
	}

	return nil
}

func (k *KlineModel) BatchInsert(klines []*Kline) error {
	batch, err := k.clickhouse.PrepareBatch(k.ctx, "INSERT INTO kline (symbol, period, timestamp, open, high, low, close, volume, amount)")
	if err != nil {
		return fmt.Errorf("prepare batch error: %v", err)
	}

	for _, kline := range klines {
		_ = batch.Append(kline.Symbol, kline.Period, kline.Timestamp, kline.Open, kline.High, kline.Low, kline.Close, kline.Volume, kline.Amount)
	}

	err = batch.Send()
	if err != nil {
		return fmt.Errorf("send batch error: %v", err)
	}

	return nil
}

func (k *KlineModel) List(symbols []string, period string, order string, limitBy, limit int) ([]Kline, error) {
	var klines []Kline
	err := k.clickhouse.Select(k.ctx, &klines, fmt.Sprintf("SELECT * FROM kline WHERE symbol IN (?) AND period =? ORDER BY timestamp %s LIMIT ? BY symbol LIMIT ?", order), symbols, period, limitBy, limit)
	if err != nil {
		return nil, fmt.Errorf("list kline error: %v", err)
	}
	return klines, nil
}

func (k *KlineModel) DeletePeriod(symbol, period string) error {
	err := k.clickhouse.Exec(k.ctx, "DELETE FROM kline WHERE symbol =? AND period =?", symbol, period)
	if err != nil {
		return fmt.Errorf("delete kline error: %v", err)
	}
	return nil
}

func (k *KlineModel) Delete(symbol string) error {
	err := k.clickhouse.Exec(k.ctx, "DELETE FROM kline WHERE symbol =?", symbol)
	if err != nil {
		return fmt.Errorf("delete kline error: %v", err)
	}
	return nil
}
